export default class Comparator {
  matcher: TypeMatcher | ValueMatcher | InstanceMatcher | PredicateMatcher | AnyMatcher
  constructor(matcher: any) {
    this.matcher = matcher
  }
  static isValue(e: any) {
    return new Comparator(new ValueMatcher(e))
  }
  static isType(e: Function) {
    return new Comparator(new TypeMatcher(e))
  }
  static isInstanceOf(e: any) {
    return new Comparator(new InstanceMatcher(e))
  }
  static is(e: (...e: any[]) => boolean) {
    return new Comparator(new PredicateMatcher(e))
  }
  static isAny() {
    return new Comparator(new AnyMatcher())
  }
  compare(e: any) {
    return this.matcher.matches(e)
  }
}
class ValueMatcher {
  value: any
  constructor(value: any) {
    this.value = value
  }
  matches(val: any) {
    return this.value === val
  }
}
class TypeMatcher {
  type: Function
  constructor(type: Function) {
    this.type = type
  }
  matches(e: Function) {
    return Object.getPrototypeOf(e).constructor === this.type
  }
}
class InstanceMatcher {
  type: any
  constructor(type: any) {
    this.type = type
  }
  matches(e: any) {
    if ("object" == typeof e) return e instanceof this.type
    const t = this.type
    switch (typeof e) {
      case "number":
        return t === Number
      case "string":
        return t === String
      case "boolean":
        return t === Boolean
      case "function":
        return t === Function
      default:
        return !1
    }
  }
}
class PredicateMatcher {
  predicate: (...e: any[]) => boolean
  constructor(e: (...e: any[]) => boolean) {
    this.predicate = e
  }
  matches(...e: any[]) {
    return this.predicate(...e)
  }
}
class AnyMatcher {
  constructor() {}
  matches() {
    return !0
  }
}
